package nachos.threads;

import nachos.machine.*;

/**
 * A <i>communicator</i> allows threads to synchronously exchange 32-bit
 * messages. Multiple threads can be waiting to <i>speak</i>, and multiple
 * threads can be waiting to <i>listen</i>. But there should never be a time
 * when both a speaker and a listener are waiting, because the two threads can
 * be paired off at this point.
 */
public class Communicator {

    boolean so1_aguardando;
    int so1_palavra_transmitida;
    int so1_numero_ouvintes;
    Lock so1_critico;
    Condition so1_orador;
    Condition so1_ouvinte;

    /**
     * Allocate a new communicator.
     */
    public Communicator() {
        Lock so1_critico = new Lock();
        Condition so1_orador = new Condition(so1_critico);
        Condition so1_ouvinte = new Condition(so1_critico);
    }

    /**
     * Wait for a thread to listen through this communicator, and then transfer
     * <i>word</i> to the listener.
     *
     * <p>
     * Does not return until this thread is paired up with a listening thread.
     * Exactly one listener should receive <i>word</i>.
     *
     * @param	word	the integer to transfer.
     */
    public void speak(int word) {
        so1_critico.acquire();

        // canal ocupado ou nao ha so1_ouvintes -> aguarda
        while (so1_aguardando || so1_numero_ouvintes == 0) {
            so1_orador.sleep();
        }

        so1_aguardando = true; // esta so1_aguardando um so1_ouvinte
        so1_palavra_transmitida = word; // transmite mensagem e informa que so1_orador 

        // espera mensagem ser recebida
        so1_ouvinte.wake();
        so1_critico.release();
    }

    /**
     * Wait for a thread to speak through this communicator, and then return the
     * <i>word</i> that thread passed to <tt>speak()</tt>.
     *
     * @return	the integer transferred.
     */
    public int listen() {
        so1_critico.acquire();

        // nao existe nada para ser escutado
        while (!so1_aguardando) {
            so1_orador.wake();
            so1_numero_ouvintes++;
            so1_ouvinte.sleep();
        }

        int word = so1_palavra_transmitida;
        so1_aguardando = false; // fim da transmissao

        // esse so1_ouvinte nao esta mais escutando
        so1_numero_ouvintes--;

        so1_critico.release();
        so1_orador.wake();

        return word;
    }
}
